﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CodeTitans.JSon;

namespace CodeTitans.JSon.Tests
{
    [TestClass]
    public class JSonWriterTests
    {
        private JSonWriter writer;

        [TestInitialize]
        public void TestInit()
        {
            writer = new JSonWriter(true);
        }

        [TestCleanup]
        public void TestClean()
        {
            Console.WriteLine("Serialized JSON object:");
            Console.WriteLine(writer.ToString());
            writer.Close();
        }

        [TestMethod]
        public void WriteAnObject()
        {
            writer.WriteObjectStart();
            writer.WriteObjectEnd();

            Console.WriteLine(writer);
            Assert.AreEqual(writer.ToString(), "{}", "Empty object expected!");
        }

        [TestMethod]
        public void WriteAnArray()
        {
            writer.WriteArrayStart();
            writer.WriteArrayEnd();

            Assert.AreEqual(writer.ToString(), "[]", "Empty array expected!");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonWriterException))]
        public void WriteInvalidMember()
        {
            writer.WriteMember("name", "value");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonWriterException))]
        public void WriteImproperObjectEnd()
        {
            writer.WriteObjectStart();
            writer.WriteArrayEnd();
        }

        [TestMethod]
        public void WriteEmbeddedObject()
        {
            writer.WriteArrayStart();
            writer.WriteObjectStart();
            writer.WriteMember("Name", "Paweł");
            writer.WriteMember("Salary", 100);
            writer.WriteMember("Company");
            writer.WriteObjectStart();
            writer.WriteMember("Name", "CodeTitans");
            writer.WriteMember("Address", "ABCD");
            writer.WriteMember("Value", 10.28437411e2);
            writer.WriteMember("Started", DateTime.Now);
            writer.WriteMemberNull("Account");
            writer.WriteObjectEnd();
            writer.WriteObjectEnd();

            writer.WriteObjectStart();
            writer.WriteMember("Name", "Aga");
            writer.WriteMember("Salary", 200);
            writer.WriteObjectEnd();

            writer.WriteValue(101);
            writer.WriteValue("New Item");

            writer.WriteArrayEnd();
        }

        [TestMethod]
        public void WriteArrayOfString()
        {
            writer.Indent = false;
            writer.Write(new string[] { "small", "medium", "large", "huge" });
        }

        [TestMethod]
        [ExpectedException(typeof(JSonWriterException))]
        public void WriteValueWithoutObject()
        {
            writer.WriteValue("AAAA");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonWriterException))]
        public void WriteValueInsideObjectInvalid()
        {
            writer.WriteObjectStart();
            writer.WriteValue("AAA");
            writer.WriteObjectEnd();
        }

        [TestMethod]
        public void TestSerializeString()
        {
            writer.Write("Paweł");
        }

        [TestMethod]
        public void WriteArrayOfInt32()
        {
            writer.WriteArrayStart();
            writer.WriteValue(123);
            writer.WriteValue(234);
            writer.WriteValue(345);
            writer.WriteValue(456);
            writer.WriteArrayEnd();
        }

        [TestMethod]
        public void WriteQuotationCheck1()
        {
            writer.Indent = false;
            writer.WriteObjectStart();
            writer.WriteMember("Name", 123);
            writer.WriteObjectEnd();

            Assert.AreEqual(writer.ToString(), "{\"Name\": 123}", "Expected formatted object with one field!");
        }

        [TestMethod]
        public void WriteQuotationCheck2()
        {
            writer.Indent = false;
            writer.WriteArrayStart();
            writer.WriteValue("ABC");
            writer.WriteArrayEnd();

            Assert.AreEqual(writer.ToString(), "[\"ABC\"]", "Expected array with one quoted element!");
        }

        [TestMethod]
        public void WriteInternalArray()
        {
            writer.WriteObjectStart();
            writer.WriteMember("Name", "Paweł");
            writer.WriteMember("Accounts");
            writer.WriteArrayStart();
            writer.WriteValue("111-222-333");
            writer.WriteValue("222-333-444");
            writer.WriteValue("333-444-555");
            writer.WriteArrayEnd();
            writer.WriteMember("Salary", 100);
            writer.WriteMember("Friends");
            writer.WriteArrayStart();
            writer.WriteObjectStart();
            writer.WriteMember("Name", "Aga");
            writer.WriteMember("Ref", 12345);
            writer.WriteObjectEnd();
            writer.WriteObjectStart();
            writer.WriteMember("Name", "Ala");
            writer.WriteMember("Ref", 23456);
            writer.WriteObjectEnd();
            writer.WriteArrayEnd();
            writer.WriteMember("Company");
            writer.WriteObjectStart();
            writer.WriteMember("Name", "CodeTitans");
            writer.WriteMember("Address", (string)null);
            writer.WriteMember("CreatedAt", DateTime.Now);
            writer.WriteObjectEnd();
            writer.WriteObjectEnd();

            Assert.IsTrue(writer.IsValid);
        }

        [TestMethod]
        public void WriteObjectMember()
        {
            writer.Indent = false;
            writer.WriteObjectStart();
            writer.WriteMember("Name");
            writer.WriteValue("Paweł");
            writer.WriteObjectEnd();

            Assert.AreEqual("{\"Name\": \"Paweł\"}", writer.ToString(), "Expected object with one member!");
        }

        [TestMethod]
        public void WriteTimeSpan()
        {
            writer.WriteArrayStart();
            writer.WriteValue(new TimeSpan(1, 1, 10));
            writer.WriteValue(new TimeSpan(100000));
            writer.WriteArrayEnd();
        }

        [TestMethod]
        public void WriteGuid()
        {
            writer.WriteArrayStart();
            writer.WriteValue(Guid.NewGuid());
            writer.WriteValue(Guid.NewGuid());
            writer.WriteValue(Guid.NewGuid());
            writer.WriteValue(Guid.NewGuid());
            writer.WriteArrayEnd();
        }

        [TestMethod]
        public void WriteAutoArray()
        {
            var array = new[] { "Paweł", "Alicja", "Gaweł", "Agnieszka" };

            writer.Indent = false;
            writer.Write(array);

            Assert.AreEqual("[\"Paweł\", \"Alicja\", \"Gaweł\", \"Agnieszka\"]", writer.ToString(), "Expected object as array value!");
        }

        [TestMethod]
        public void WriteAutoDictArray()
        {
            var data = new Dictionary<string, object>();
            var complex = new Dictionary<string, string>();

            complex.Add("Name", "Piotr");
            complex.Add("id", "id-abcd");

            data.Add("Name", "Paweł");
            data.Add("Age", 29);
            data.Add("Salary", 123.456);
            data.Add("BirthDate", new DateTime(1981, 8, 18));
            data.Add("Relatives", new object[] { "Agnieszka", "Alicja", "Zdzisław", "Wiesława", complex });

            writer.Indent = false;
            writer.Write(data);

            Assert.AreEqual("{\"Name\": \"Paweł\", \"Age\": 29, \"Salary\": 123.456, \"BirthDate\": \"1981-08-17 22:00:00Z\", \"Relatives\": [\"Agnieszka\", \"Alicja\", \"Zdzisław\", \"Wiesława\", {\"Name\": \"Piotr\", \"id\": \"id-abcd\"}]}", writer.ToString(), "Expected formatted object!");
        }
    }
}
